/*
  提示：
  ws2812 8*8硬屏
  开发板选择 NodeMCU 1.0

*/

#include "config.h"


// handle WebSocket message
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      Serial.printf("[%u] Disconnected!\n", num);
      break;
    case WStype_CONNECTED: {
        IPAddress ip = webSocket.remoteIP(num);
        //Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
        // send message to client
        webSocket.sendTXT(num, "Connected");
      }
      break;
    case WStype_TEXT:
    {
      Serial.printf("[%u] get Text[%u].\n", num, length);
      strcpy(socketMessage,(char *)payload);
      if (socketMessage[0] == 'l')//切换风格
      {
          modeChange();
      }else if (socketMessage[0] == 'b')//亮度
      {
         brightnessChange();
      }else if (socketMessage[0] == 'c')//颜色
      {
        if(socketMessage[1] == 'c'){
          clockChange();
        }else{
          colorChange();
        }
      }else if (socketMessage[0] == 's')//状态
      {
         statusSave();
      }else{
        String txt = String();
        Serial.println(*payload);
        uint8_t i = 0;
        for (uint8_t y = 0; y < yres; y++) {
          for (uint8_t x = 0; x < xres; x++) {
            uint8_t r = payload[y*xres*3+x*3]*2;
            uint8_t g = payload[y*xres*3+x*3+1]*2;
            uint8_t b = payload[y*xres*3+x*3+2]*2;
            matrix.drawPixel(x,y,matrix.Color(r,g,b));
            //Serial.printf("(%u, %u) color: %u, %u, %u\n", x, y, r, g, b);
          }
        }
        matrix.show(); // This sends the updated pixel color to the hardware.
      }
    }
    break;
  }
}

void setup() {
  Serial.begin(115200);
  EEPROM.begin(16);
  // Check if this is first time EEPROM is used
  
  matrix.begin();
  matrix.setTextWrap(false);
  matrix.setBrightness(50);
  matrix.show(); // This sends the updated pixel color to the hardware.
  helloWorld();
  showWiFi();
  //WiFiManager
  WiFiManager wifiManager;
  wifiManager.autoConnect(WIFI_NAME);
  // start MDNS
  if (MDNS.begin(MDNS_NAME)) {
    Serial.println("MDNS responder started.");
  }
  button.attachClick(colorChange);
  button.attachDoubleClick(modeChange);
  button.attachMultiClick(statusSave);
  button.attachDuringLongPress(brightnessChange);
  // start web server
  // handle index
  server.on("/", []() {
    // send index.html
    server.send(200, "text/html", "<!DOCTYPE html><html><head> <meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> <meta name='viewport' content='user-scalable=no,initial-scale=2.0' /> <style> body { background: #000; color: #fff; text-align: center } div { margin: 10px } </style></head><body> <div> <h3>Pick An Emoji</h3> <img id='emojis' onclick='clickEmoji(event);' src=''> </div> <div> <p id='out'></p> <canvas id='emoji' width='8' height='8'></canvas> </div> <div> <button onclick='modeChange()'>模式切换</button> <button onclick='brightnessChange()'>亮度调节</button> <button onclick='colorChange()'>颜色变更</button> <button onclick='statusSave()'>配置保存</button> <button onclick='clockChange()'>时钟样式</button> </div> <script> function nw() { return new WebSocket('ws://' + location.hostname + ':81/', ['arduino']); } var ws = nw();  var sk = 0; function cc(c) { return String.fromCharCode(c / 2); } function clickEmoji(e) {  var xo = 3; var yo = 3; var xs = 13; var ys = 12; var x = e.offsetX; var y = e.offsetY;  var c = Math.round((x - xo - 4) / xs);  var r = Math.round((y - yo - 4) / ys);  document.getElementById('out').innerText = 'Selected [' + c + ', ' + r + ']';  var img = document.getElementById('emojis');  var ce = document.getElementById('emoji');  var ctx = ce.getContext('2d');  ctx.drawImage(img, -(xo + (xs * c)), -(yo + (ys * r)), 131, 122); var d = ctx.getImageData(0, 0, 8, 8).data;  var t = '';  for (var i = 0; i < 8; i++) { for(var j = 0; j < 8; j++){ var imageData = ctx.getImageData(j, i, 1, 1).data; t += cc(imageData[0]); t += cc(imageData[1]); t += cc(imageData[2]); } } ws.send(t, { binary: true }); } function modeChange(){ console.log('切换模式'); ws.send('l'); } function brightnessChange(){ console.log('亮度调节'); ws.send('b'); } function colorChange(){ console.log('颜色变更'); ws.send('c'); } function statusSave(){ console.log('保存配置'); ws.send('s'); } function clockChange(){ console.log('时钟样式'); ws.send('cc'); } </script> </body> </html>");
    Serial.println("index.html sent.");
  });
  server.begin();

  // start webSocket server
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
  // Add service to MDNS
  MDNS.addService("http", "tcp", 80);
  MDNS.addService("ws", "tcp", 81);
  if (EEPROM.read(15) != 123) {  // not equal to 123
    Serial.println("未查到默认配置");
    Mode = 0;
    color_index = 0;
    brightnessNow = 50;
    clockMode = 1;
  } else {  // if byte 256 is equal to 123
    Serial.println("存有默认配置");
    Mode = int(EEPROM.read(0));  // get previous lit leds before power failure
    color_index = int(EEPROM.read(1));
    brightnessNow = int(EEPROM.read(2));
    clockMode = int(EEPROM.read(3));
  }
  matrix.setBrightness(brightnessNow);
  matrix.show();

  ShowIP();
  switchMode(true);
}


unsigned long lastTime = 0;
unsigned long lastColorTime = 0;
unsigned int counter = 0;

//模式变更
void modeChange()
{
  matrix.clear();
  if (++Mode > 2)Mode = 0;//模式变化
  Serial.println("按键切换模式");
  Serial.println(Mode);
  switchMode(true);
}
//时钟样式切换
void clockChange(){
  matrix.clear();
  if (++clockMode > 1)clockMode = 0;//时钟变化
  Serial.println("按键切换时钟");
  Serial.println(clockMode);
  switchMode(true);
}
//颜色模式变更
void colorChange()
{
  if (++color_index > 3)color_index = 0;//模式变化
  Serial.println("按键切换颜色");
  Serial.println(color_index);
  switchMode(true);
}
//亮度调节
void brightnessChange()
{
  unsigned long t = millis();
  if ((t - lastTime) > 500) {
    brightnessNow += 10;
    if(brightnessNow > 100){
      brightnessNow = 10;
    }
    matrix.setBrightness(brightnessNow);
    matrix.show();
    lastTime = millis();
  }
}


//RGB颜色数组https://www.sojson.com/rgb.html
const ColorRGB myRGBColorPalette[4][2] = {
  {ColorVarInit(255,102,0), ColorVarInit(30,144,255)},
  {ColorVarInit(255,255,0), ColorVarInit(255,0,77)},
  {ColorVarInit(0,255,0), ColorVarInit(148,0,211)},
  {ColorVarInit(0,255,255), ColorVarInit(255,0,255)}
};

//设置保存
void statusSave(){
  Serial.println("按键保存配置");
  if (EEPROM.read(15) != 123) {  // not equal to 123
    EEPROM.write(15, 123);  // write value 123 to byte 256
    EEPROM.write(0, Mode);
    EEPROM.write(1, color_index);
    EEPROM.write(2, brightnessNow);
    EEPROM.write(3, clockMode);
  } else {  // if byte 256 is equal to 123
      EEPROM.write(0, Mode);
      EEPROM.write(1, color_index);
      EEPROM.write(2, brightnessNow);
      EEPROM.write(3, clockMode);
  }
  EEPROM.commit();
  int x = xres;
  for(int i=0;i<36;i++){
    matrix.clear();
    matrix.fillScreen(0);
    matrix.setCursor(x, 0);
    matrix.print(F("Saved"));
    matrix.setTextColor(matrix.Color(148,0,211));
    if(--x < -36) {
      x = matrix.width();
    }
    matrix.show();
    system_soft_wdt_feed();
    delay(70);
  }
  matrix.clear();
  switchMode(true);
}

//显示画板功能
void showDrawingBoard(bool implement) {

  unsigned long t = millis();
  if(implement){
    matrix.clear();
    showEmoji();
    matrix.show();
  }
  if ((t - lastTime) > 10 * 1000 | implement) {
    counter++;
    bool ping = (counter % 2);
    int i = webSocket.connectedClients(ping);
    Serial.printf("%d Connected websocket clients ping: %d\n", i, ping);
    lastTime = millis();
  }
}

void showEmoji(){
    for (uint8_t y = 0; y < yres; y++) {
          for (uint8_t x = 0; x < xres; x++) {
              uint8_t r = pgm_read_dword(&(emoji[y*xres*3+x*3]));
              uint8_t g = pgm_read_dword(&(emoji[y*xres*3+x*3+1]));
              uint8_t b = pgm_read_dword(&(emoji[y*xres*3+x*3+2]));
              matrix.drawPixel(x,y,matrix.Color(r,g,b));
          }
    }  
}

void showTime(bool implement)
{
  timeClient.update();
  H = timeClient.getFormattedTime().substring(0, 2);
  M = timeClient.getFormattedTime().substring(3, 5);
  if ( millis() - lastColorTime > 300 | implement)
  {
    if(!implement){
      lastColorTime = millis();
    }
    switch(clockMode){
      case 0:
        colorLoop = colorLoop+1;
        if(colorLoop>28){
            colorLoop = 1;
        }
        showHours();
        showbittime(M,3);//pad.Color(0, 20, 10, 20)
        break;
      case 1:
        S = timeClient.getFormattedTime().substring(6, 8);
        binaryClock();
        break;
    }
    
    matrix.show();
  }
}


void binaryClock(){
  int h1,h2;
  int s1,s2;
  int m1,m2;
  if(H.toInt()<10){
    h1 = 0;
    h2 = (String(H.charAt(1)).toInt() + 1) - 1; 
  }else{
    h1 = (String(H.charAt(0)).toInt() + 1) - 1;
    h2 = (String(H.charAt(1)).toInt() + 1) - 1;
  }
  showBinary(h1, 0, 20);
  showBinary(h2, 1, 20);

  if(M.toInt()<10){
    m1 = 0;
    m2 = (String(M.charAt(1)).toInt() + 1) - 1; 
  }else{
    m1 = (String(M.charAt(0)).toInt() + 1) - 1;
    m2 = (String(M.charAt(1)).toInt() + 1) - 1;
  }
  showBinary(m1, 3, 120);
  showBinary(m2, 4, 120);

  if(S.toInt()<10){
    s1 = 0;
    s2 = (String(S.charAt(1)).toInt() + 1) - 1; 
  }else{
    s1 = (String(S.charAt(0)).toInt() + 1) - 1;
    s2 = (String(S.charAt(1)).toInt() + 1) - 1;
  }
  Serial.println(s1);
  Serial.println(s2);
  showBinary(s1, 6, 220);
  showBinary(s2, 7, 220);
  
}

void showBinary(int num, int x, int hue){
  for(int i=0;i<4;i++){
    if (String(binarydata[num].charAt(i)).toInt() != 0) {
      matrix.drawPixel(x,2+i,hsv2rgb(hue,100,100));
    } else {
      matrix.drawPixel(x,2+i,hsv2rgb(hue,60,50));
    }
  }
}

void showBinary2(int num, int x, uint16_t colorxy){
  for(int i=0;i<4;i++){
    if (String(binarydata[num].charAt(i)).toInt() != 0) {
      drawFastYLine(x, (i+1)*2-2, 2, colorxy);
    } else {
      drawFastYLine(x, (i+1)*2-2, 2, pad.Color(0, 0, 0, 0));
    }
  }
}

void showHours(){
  int hours = timeClient.getHours();
  uint16_t colorxy = pad.Color(0, 210, 0, 230);  
  if(hours>12){
    hours = hours-12;
  }
  drawFastXLineGXY(0,0,8,false);
  drawFastXLineGXY(0,1,8,false);
  if(hours<7){
    drawFastXLineGXY(1,0,hours,true);
  }else{
    drawFastXLineGXY(1,0,6,true);
    drawFastXLineGXY(1,1,hours-6,true);
  }
}

void showbittime(String nowtime, int y){
  if(nowtime.toInt()<10){
    showbitmap8(bitdata20[(int)(0)],4,5, 0, y);
    showbitmap8(bitdata20[(int)((String(nowtime.charAt(1)).toInt() + 1) - 1)], 4, 5, 4, y);
  }else{
    showbitmap8(bitdata20[(int)((String(nowtime.charAt(0)).toInt() + 1) - 1)],4,5, 0, y);
    showbitmap8(bitdata20[(int)((String(nowtime.charAt(1)).toInt() + 1) - 1)],4,5, 4, y);
  }
 }

void showbitmap(String bitrgbstr, int x, int y, uint16_t colorxy) {
  for (int i = x; i < x+(4); i = i + (1)) {
    for(int j = y; j < y+(4); j = j + (1)){
      if (String(bitrgbstr.charAt(((j-y)*4+i-x))).toInt() != 0) {
        matrix.drawPixel(i,j,colorxy);
      } else {
        matrix.drawPixel(i,j,pad.Color(0, 0, 0, 0));
      }
    }
  }
}

void showbitmap8(String bitrgbstr, int xlength, int ylength, int x, int y) {
  //Serial.println("bitrgbstr = " + bitrgbstr);
  for (int i = x; i < x+(xlength); i = i + (1)) {
    for(int j = y; j < y+(ylength); j = j + (1)){
      if (String(bitrgbstr.charAt(((j-y)*xlength+i-x))).toInt() != 0) {
        matrix.drawPixel(i,j,Gradient(i,j));
      } else {
        matrix.drawPixel(i,j,pad.Color(0, 0, 0, 0));
      }
    }
  }
}

void showFFT() {
  //Collect Samples
    getSamples();
    //Update Display
    displayUpdate();
    matrix.show();
    delay(1);
}

void getSamples(){
  for(int i = 0; i < SAMPLES; i++){
    vReal[i] = analogRead(MIC_IN);
    Serial.println(vReal[i]);
    vImag[i] = 0;
  }
  //FFT
  FFT.Windowing(vReal, 1, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
  //Update Intensity Array
  for(int i = 2; i < (xres*Displacement)+2; i+=Displacement){
    vReal[i] = constrain(vReal[i],freq_gain2[i-2] ,1300);            // set max value for input data
    vReal[i] = map(vReal[i], freq_gain2[i-2], 1200, 0, yres);        // map data to fit our display
    Intensity[(i/Displacement)-2] --;                      // Decrease displayed value
    if (vReal[i] > Intensity[(i/Displacement)-2])          // Match displayed value to measured value
      Intensity[(i/Displacement)-2] = vReal[i];
  }
}


void displayUpdate(){
  int color = 0;
  for(int i = 0; i < xres; i++){
    CRGB c_color = CHSV(color, 255, 255);
    if(color_index>0){
      drawFastYLine(i,yres-Intensity[i],Intensity[i],matrix.Color(c_color.r,c_color.g,c_color.b));
    }else{
      drawFastYLine(i,yres-Intensity[i],Intensity[i],matrix.Color(c_color.r,c_color.g,c_color.b));
    }  
    drawFastYLine(i,0,yres-1-Intensity[i],matrix.Color(0,0,0));

    color += 255/xres;             // Increment the Hue to get the Rainbow
    if(color>255){
      color = 0;
    }
  } 
}

void switchMode(bool change){
  switch (Mode)
  {
    case 0:
      showDrawingBoard(change);
      break;
    case 1:
      showTime(change);
      break;
    case 2:
      showFFT();
      break;
  }
}

void loop()
{
  button.tick();
  MDNS.update();
  webSocket.loop();
  server.handleClient();
  switchMode(false);
}
